<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\ckpt;

use lflow\impl\DefaultNoGenerator;
use lflow\INoGenerator;
use lflow\lib\interface\Completion;
use lflow\lib\util\AssertHelper;
use lflow\lib\util\ClassUtil;
use lflow\lib\util\ObjectHelper;
use lflow\lib\util\StringHelper;
use ReflectionClass;

/**
 * 流程定义
 *
 * @author Mr.April
 * @since  1.0
 */
class ProcessCkpt extends BaseCkpt
{
    /**
     * 节点元素集合
     */
    private array $nodes = [];
    private array $taskModels = [];

    /**
     * 流程实例启动url
     */
    private string $instanceUrl;

    /**
     * 期望完成时间
     */
    private string $expireTime;

    /**
     * 实例编号生成的class
     */
    private string $instanceNoClass;

    /**
     * 实例编号生成器对象
     */
    private ?INoGenerator $generator;

    /**
     * 回调class
     */
    private string|null $callback;

    /**
     * 回调对象
     *
     * @var \lflow\lib\interface\Completion|null
     */
    private ?Completion $callbackObject;

    /**
     * 返回当前流程定义的所有工作任务节点模型
     *
     * @return array
     */
    public function getWorkModels(): array
    {
        $models = [];
        foreach ($this->nodes as $node) {
            if ($node instanceof WorkModel) {
                $models[] = $node;
            }
        }
        return $models;
    }

    /**
     * 获取所有的有序任务模型集合
     *
     * @return array
     */
    public function getTaskModels(): array
    {
        if ($this->taskModels) {
            $this->synchronized(function () {
                if (empty($this->taskModels)) {
                    $this->buildModels($this->taskModels, $this->getStart()->getNextModels(TaskModel::class), TaskModel::class);
                }
            });
        }
        return $this->taskModels;
    }

    /**
     * getModels  根据指定的节点类型返回流程定义中所有模型对象
     *
     * @param object $clazz 节点模型
     *
     * @return array
     */
    public function getCkpts(object $clazz): array
    {
        $models = [];
        $this->buildCkpts($models, $this->getStart()->getNextCkpts($clazz), $clazz);
        return $models;
    }

    private function buildCkpts(array &$models, array $nextModels, object $clazz): void
    {
        foreach ($nextModels as $nextModel) {
            $models[] = $nextModel;
        }
    }

    private function nextCkpts()
    {

    }

    /**
     * 获取process定义的start节点模型
     *
     * @return \lflow\ckpt\StartCkpt|null
     */
    public function getStart(): ?StartCkpt
    {
        foreach ($this->nodes as $node) {
            if ($node instanceof StartCkpt) {
                return $node;
            }
        }
        return null;
    }

    /**
     * 获取process定义的指定节点名称的节点模型
     *
     * @param string $nodeName
     *
     * @return \lflow\ckpt\NodeCkpt|null
     */
    public function getNode(string $nodeName): NodeCkpt|null
    {
        foreach ($this->nodes as $node) {
            if (StringHelper::equalsIgnoreCase($node->getName(), $nodeName)) {
                return $node;
            }
        }
        return null;
    }

    /**
     * 判断当前模型的节点是否包含给定的节点名称参数
     *
     * @param        $class
     * @param string $nodeNames
     *
     * @return bool
     * @throws \ReflectionException
     */
    public function containsNodeNames($class, string $nodeNames): bool
    {
        $t = new ReflectionClass(new $class());
        foreach ($this->nodes as $node) {
            if (!$t->isInstance($node)) {
                continue;
            }
            foreach (explode(',', $nodeNames) as $nodeName) {
                if (StringHelper::equalsIgnoreCase($node->getName(), $nodeName)) {
                    return true;
                };
            }
        }
        return false;
    }

    public function getNodes(): array
    {
        return $this->nodes;
    }

    public function setNodes($nodes): void
    {
        $this->nodes = $nodes;
    }

    public function getExpireTime(): string
    {
        return $this->expireTime ?? '';
    }

    public function setExpireTime(string $expireTime)
    {
        $this->expireTime = $expireTime;
    }

    public function getInstanceUrl(): string
    {
        return $this->instanceUrl;
    }

    public function setInstanceUrl(string $instanceUrl): void
    {
        $this->instanceUrl = $instanceUrl;
    }

    public function getInstanceNoClass(): string
    {
        return $this->instanceNoClass;
    }

    public function setInstanceNoClass(string $instanceNoClass): void
    {
        $this->instanceNoClass = $instanceNoClass;
        if (StringHelper::isNotEmpty($instanceNoClass)) {
            //第三方class
            $this->generator = app()->make($instanceNoClass);
        }
    }

    public function getGenerator(): INoGenerator
    {
        return !isset($this->generator) ? new DefaultNoGenerator() : $this->generator;
    }

    public function setGenerator(INoGenerator $generator): void
    {
        $this->generator = $generator;
    }

    public function setCallback(string|null $callback): void
    {
        $count          = 1;
        $classZ         = str_replace('/', '\\', empty($callback) ? '' : $callback, $count);
        $this->callback = $classZ;
        if (StringHelper::isNotEmpty($classZ)) {
            $callbackObject = ClassUtil::instantiateClass($classZ);
            AssertHelper::notNull($callbackObject, $classZ . 'class 不存在');
            //第三方class
            $this->callbackObject = $callbackObject;
        }
    }

    public function getCallback(): string

    {
        return !isset($this->callback) ? $this->callback : '';
    }

    public function getCompletion(): ?Completion
    {
        return $this->callbackObject ?? null;
    }

}
